﻿Imports System.ComponentModel
Friend Delegate Function CanResetValueHandler(ByVal component As Object) As Boolean
Friend Delegate Function GetChildPropertiesHandler(ByVal instance As Object, ByVal filter As Attribute()) As PropertyDescriptorCollection
Friend Delegate Function GetValueHandler(ByVal component As Object) As Object
Friend Delegate Sub ResetValueHandler(ByVal component As Object)
Friend Delegate Sub SetValueHandler(ByVal component As Object, ByVal value As Object)
Friend Delegate Function ShouldSerializeValueHandler(ByVal component As Object) As Boolean

Friend Class DynamicPropertyDescriptor
    Inherits PropertyDescriptor
    ' Methods
    Public Sub New(ByVal baseDescriptor As PropertyDescriptor)
        Me.New(baseDescriptor, Nothing)
    End Sub

    Public Sub New(ByVal name As String)
        MyBase.New(name, Nothing)
    End Sub

    Public Sub New(ByVal baseDescriptor As PropertyDescriptor, ByVal ParamArray newAttributes As Attribute())
        MyBase.New(baseDescriptor, newAttributes)
        Me.AttributeArray = DynamicPropertyDescriptor.FilterAttributes(Me.AttributeArray)
        Me._baseDescriptor = baseDescriptor
    End Sub

    Public Sub New(ByVal name As String, ByVal ParamArray attributes As Attribute())
        MyBase.New(name, DynamicPropertyDescriptor.FilterAttributes(attributes))
    End Sub

    Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
        If (Not Me.CanResetValueHandler Is Nothing) Then
            Return Me.CanResetValueHandler.Invoke(component)
        End If
        If (Not Me._baseDescriptor Is Nothing) Then
            Return Me._baseDescriptor.CanResetValue(component)
        End If
        Return (Not Me.Attributes.Item(GetType(DefaultValueAttribute)) Is Nothing)
    End Function

    Private Shared Function FilterAttributes(ByVal attributes As Attribute()) As Attribute()
        Dim dictionary As New Dictionary(Of Object, Attribute)
        Dim attribute As Attribute
        For Each attribute In attributes
            If Not attribute.IsDefaultAttribute Then
                dictionary.Add(attribute.TypeId, attribute)
            End If
        Next
        Dim array As Attribute() = New Attribute(dictionary.Values.Count - 1) {}
        dictionary.Values.CopyTo(array, 0)
        Return array
    End Function

    Public Overrides Function GetChildProperties(ByVal instance As Object, ByVal filter As Attribute()) As PropertyDescriptorCollection
        If (Not Me.GetChildPropertiesHandler Is Nothing) Then
            Return Me.GetChildPropertiesHandler.Invoke(instance, filter)
        End If
        If (Not Me._baseDescriptor Is Nothing) Then
            Return Me._baseDescriptor.GetChildProperties(instance, filter)
        End If
        Return MyBase.GetChildProperties(instance, filter)
    End Function

    Public Overrides Function GetValue(ByVal component As Object) As Object
        If (Not Me.GetValueHandler Is Nothing) Then
            Return Me.GetValueHandler.Invoke(component)
        End If
        If (Not Me._baseDescriptor Is Nothing) Then
            Return Me._baseDescriptor.GetValue(component)
        End If
        Return Nothing
    End Function

    Public Overrides Sub ResetValue(ByVal component As Object)
        If (Not Me.ResetValueHandler Is Nothing) Then
            Me.ResetValueHandler.Invoke(component)
        ElseIf (Not Me._baseDescriptor Is Nothing) Then
            Me._baseDescriptor.ResetValue(component)
        Else
            Dim attribute As DefaultValueAttribute = TryCast(Me.Attributes.Item(GetType(DefaultValueAttribute)), DefaultValueAttribute)
            If (Not attribute Is Nothing) Then
                Me.SetValue(component, attribute.Value)
            End If
        End If
    End Sub

    Public Sub SetAttribute(ByVal value As Attribute)
        If (value Is Nothing) Then
            Throw New ArgumentNullException("value")
        End If
        If (Me._attributes Is Nothing) Then
            Me._attributes = New List(Of Attribute)
        End If
        Me._attributes.Add(value)
    End Sub

    Public Sub SetAttributes(ByVal ParamArray values As Attribute())
        Dim attribute As Attribute
        For Each attribute In values
            Me.SetAttribute(attribute)
        Next
    End Sub

    Public Sub SetCategory(ByVal value As String)
        If (value Is Nothing) Then
            value = CategoryAttribute.Default.Category
        End If
        Me._category = value
        Me.SetAttribute(New CategoryAttribute(value))
    End Sub

    Public Sub SetComponentType(ByVal value As Type)
        Me._componentType = value
    End Sub

    Public Sub SetConverterType(ByVal value As Type)
        Me._converterTypeName = IIf((Not value Is Nothing), value.AssemblyQualifiedName, Nothing)
        If (Not Me._converterTypeName Is Nothing) Then
            Me.SetAttribute(New TypeConverterAttribute(value))
        Else
            Me.SetAttribute(TypeConverterAttribute.Default)
        End If
        Me._converter = Nothing
    End Sub

    Public Sub SetDescription(ByVal value As String)
        If (value Is Nothing) Then
            value = DescriptionAttribute.Default.Description
        End If
        Me._description = value
        Me.SetAttribute(New DescriptionAttribute(value))
    End Sub

    Public Sub SetDesignTimeOnly(ByVal value As Boolean)
        Me.SetAttribute(New DesignOnlyAttribute(value))
    End Sub

    Public Sub SetDisplayName(ByVal value As String)
        If (value Is Nothing) Then
            value = DisplayNameAttribute.Default.DisplayName
        End If
        Me.SetAttribute(New DisplayNameAttribute(value))
    End Sub

    Public Sub SetIsBrowsable(ByVal value As Boolean)
        Me.SetAttribute(New BrowsableAttribute(value))
    End Sub

    Public Sub SetIsLocalizable(ByVal value As Boolean)
        Me.SetAttribute(New LocalizableAttribute(value))
    End Sub

    Public Sub SetIsReadOnly(ByVal value As Boolean)
        Me.SetAttribute(New ReadOnlyAttribute(value))
    End Sub

    Public Sub SetName(ByVal value As String)
        If (value Is Nothing) Then
            value = String.Empty
        End If
        Me._name = value
    End Sub

    Public Sub SetPropertyType(ByVal value As Type)
        If (value Is Nothing) Then
            Throw New ArgumentNullException("value")
        End If
        Me._propertyType = value
    End Sub

    Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
        If (Not Me.SetValueHandler Is Nothing) Then
            Me.SetValueHandler.Invoke(component, value)
            Me.OnValueChanged(component, EventArgs.Empty)
        ElseIf (Not Me._baseDescriptor Is Nothing) Then
            Me._baseDescriptor.SetValue(component, value)
            Me.OnValueChanged(component, EventArgs.Empty)
        End If
    End Sub

    Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
        If (Not Me.ShouldSerializeValueHandler Is Nothing) Then
            Return Me.ShouldSerializeValueHandler.Invoke(component)
        End If
        If (Not Me._baseDescriptor Is Nothing) Then
            Return Me._baseDescriptor.ShouldSerializeValue(component)
        End If
        Dim attribute As DefaultValueAttribute = TryCast(Me.Attributes.Item(GetType(DefaultValueAttribute)), DefaultValueAttribute)
        Return ((Not attribute Is Nothing) AndAlso Not Object.Equals(Me.GetValue(component), attribute.Value))
    End Function


    ' Properties
    Public Overrides ReadOnly Property Attributes As AttributeCollection
        Get
            If (Not Me._attributes Is Nothing) Then
                Dim dictionary As New Dictionary(Of Object, Attribute)
                Dim attribute As Attribute
                For Each attribute In Me.AttributeArray
                    dictionary.Item(attribute.TypeId) = attribute
                Next
                Dim attribute2 As Attribute
                For Each attribute2 In Me._attributes
                    If Not attribute2.IsDefaultAttribute Then
                        dictionary.Item(attribute2.TypeId) = attribute2
                    ElseIf dictionary.ContainsKey(attribute2.TypeId) Then
                        dictionary.Remove(attribute2.TypeId)
                    End If
                    Dim attribute3 As CategoryAttribute = TryCast(attribute2, CategoryAttribute)
                    If (Not attribute3 Is Nothing) Then
                        Me._category = attribute3.Category
                    End If
                    Dim attribute4 As DescriptionAttribute = TryCast(attribute2, DescriptionAttribute)
                    If (Not attribute4 Is Nothing) Then
                        Me._description = attribute4.Description
                    End If
                    Dim attribute5 As TypeConverterAttribute = TryCast(attribute2, TypeConverterAttribute)
                    If (Not attribute5 Is Nothing) Then
                        Me._converterTypeName = attribute5.ConverterTypeName
                        Me._converter = Nothing
                    End If
                Next
                Dim array As Attribute() = New Attribute(dictionary.Values.Count - 1) {}
                dictionary.Values.CopyTo(array, 0)
                Me.AttributeArray = array
                Me._attributes = Nothing
            End If
            Return MyBase.Attributes
        End Get
    End Property

    Public Property CanResetValueHandler As CanResetValueHandler
        Get
            Return Me._canResetValueHandler
        End Get
        Set(ByVal value As CanResetValueHandler)
            Me._canResetValueHandler = value
        End Set
    End Property

    Public Overrides ReadOnly Property Category As String
        Get
            If (Not Me._category Is Nothing) Then
                Return Me._category
            End If
            Return MyBase.Category
        End Get
    End Property

    Public Overrides ReadOnly Property ComponentType As Type
        Get
            If (Not Me._componentType Is Nothing) Then
                Return Me._componentType
            End If
            If (Not Me._baseDescriptor Is Nothing) Then
                Return Me._baseDescriptor.ComponentType
            End If
            Return Nothing
        End Get
    End Property

    Public Overrides ReadOnly Property Converter As TypeConverter
        Get
            If (Not Me._converterTypeName Is Nothing) Then
                If (Me._converter Is Nothing) Then
                    Dim typeFromName As Type = MyBase.GetTypeFromName(Me._converterTypeName)
                    If GetType(TypeConverter).IsAssignableFrom(typeFromName) Then
                        Me._converter = DirectCast(MyBase.CreateInstance(typeFromName), TypeConverter)
                    End If
                End If
                If (Not Me._converter Is Nothing) Then
                    Return Me._converter
                End If
            End If
            Return MyBase.Converter
        End Get
    End Property

    Public Overrides ReadOnly Property Description As String
        Get
            If (Not Me._description Is Nothing) Then
                Return Me._description
            End If
            Return MyBase.Description
        End Get
    End Property

    Public Property GetChildPropertiesHandler As GetChildPropertiesHandler
        Get
            Return Me._getChildPropertiesHandler
        End Get
        Set(ByVal value As GetChildPropertiesHandler)
            Me._getChildPropertiesHandler = value
        End Set
    End Property

    Public Property GetValueHandler As GetValueHandler
        Get
            Return Me._getValueHandler
        End Get
        Set(ByVal value As GetValueHandler)
            Me._getValueHandler = value
        End Set
    End Property

    Public Overrides ReadOnly Property IsReadOnly As Boolean
        Get
            Return ReadOnlyAttribute.Yes.Equals(Me.Attributes.Item(GetType(ReadOnlyAttribute)))
        End Get
    End Property

    Public Overrides ReadOnly Property Name As String
        Get
            If (Not Me._name Is Nothing) Then
                Return Me._name
            End If
            Return MyBase.Name
        End Get
    End Property

    Protected Overrides ReadOnly Property NameHashCode As Integer
        Get
            If (Not Me._name Is Nothing) Then
                Return Me._name.GetHashCode
            End If
            Return MyBase.NameHashCode
        End Get
    End Property

    Public Overrides ReadOnly Property PropertyType As Type
        Get
            If (Not Me._propertyType Is Nothing) Then
                Return Me._propertyType
            End If
            If (Not Me._baseDescriptor Is Nothing) Then
                Return Me._baseDescriptor.PropertyType
            End If
            Return Nothing
        End Get
    End Property

    Public Property ResetValueHandler As ResetValueHandler
        Get
            Return Me._resetValueHandler
        End Get
        Set(ByVal value As ResetValueHandler)
            Me._resetValueHandler = value
        End Set
    End Property

    Public Property SetValueHandler As SetValueHandler
        Get
            Return Me._setValueHandler
        End Get
        Set(ByVal value As SetValueHandler)
            Me._setValueHandler = value
        End Set
    End Property

    Public Property ShouldSerializeValueHandler As ShouldSerializeValueHandler
        Get
            Return Me._shouldSerializeValueHandler
        End Get
        Set(ByVal value As ShouldSerializeValueHandler)
            Me._shouldSerializeValueHandler = value
        End Set
    End Property


    ' Fields
    Private _attributes As List(Of Attribute)
    Private _baseDescriptor As PropertyDescriptor
    Private _canResetValueHandler As CanResetValueHandler
    Private _category As String
    Private _componentType As Type
    Private _converter As TypeConverter
    Private _converterTypeName As String
    Private _description As String
    Private _getChildPropertiesHandler As GetChildPropertiesHandler
    Private _getValueHandler As GetValueHandler
    Private _name As String
    Private _propertyType As Type
    Private _resetValueHandler As ResetValueHandler
    Private _setValueHandler As SetValueHandler
    Private _shouldSerializeValueHandler As ShouldSerializeValueHandler
End Class


